#!/usr/bin/python

from gettext import gettext as _
import rpm
import os
import sys
from getopt import getopt
def usage():
    print '%s -t <target sys root> -s<packages dir> -p <path of pkgarr.py> [mount | umount] [ -l <stdout log file> -e <stderr log file> ]\n' % sys.argv[0], \
          '\t install the packages into target system root order by pkgarr.py(from MI)\n', \
          '\t if use mount(umount) arg, only mount(umount) the virtual system on target sys root'

args = sys.argv
arg_map = {}
for arg in args:
    if arg == 'help' or \
            arg == '--help' or \
            arg == '-help' or \
            arg == '-h':
        usage()
        sys.exit(0)
    if arg == 'mount':
        arg_map['mount'] = 'y'
    if arg == 'umount':
        arg_map['umount'] = 'y'
new_args = []
for arg in args:
    if not arg_map.has_key(arg):
        new_args.append(arg)
sys.argv = new_args


try:
    import pkgpublic
    import isys
except ImportError:
    if os.path.exists('scripts'):
        sys.path.insert(0, 'scripts')
        sys.path.insert(0, 'libs')
    if os.path.exists('../scripts'):
        sys.path.insert(0, '../scripts')
        sys.path.insert(0, '../libs')
    try:
        import pkgpublic
        import isys
    except ImportError:
        print 'Can not import pkgpublic and isys'
        sys.exit(1)

tgtsys_root = ''
pkgs_root = ''
pkgarr_path = ''
stdout_f = ''
stderr_f = ''
try:
    optlist, args = getopt(sys.argv[1:], 't:s:p:l:e:')
except:
    usage()
    sys.exit(1)
for (optname, optval) in optlist:
    if optname == '-t':
        tgtsys_root = optval
    if optname == '-s':
        pkgs_root = optval
    if optname == '-p':
        pkgarr_path = optval
    if optname == '-l':
        stdout_f = optval
    if optname == '-e':
        stderr_f = optval

if not tgtsys_root or not pkgs_root or not pkgarr_path:
    usage()
    sys.exit(1)

f_log1 = True
f_log2 = True
if not stdout_f:
    f_log1 = False
if not stderr_f:
    f_log2 = False

tgtsys_root = os.path.abspath(tgtsys_root)
pkgs_root = os.path.abspath(pkgs_root)
pkgarr_path = os.path.abspath(pkgarr_path)
stdout_f = os.path.abspath(stdout_f)
stderr_f = os.path.abspath(stderr_f)

# log the stdout
logfd_out = None
def dolog(str):
    global logfd_out
    if f_log1:
        if logfd_out == None:
            try:
                logfd_out = file(stdout_f, 'w')
            except:
                print 'Open log file %s failed.\n' % stdout_f
                logfd_out = None
        else:
            logfd_out.write(str)
            logfd_out.flush()
    print str,
# log the stderr
if f_log2:
    logfd_err = os.open(os.path.join(os.curdir, stderr_f), os.O_CREAT |
            os.O_WRONLY, 0600)
    os.dup2(logfd_err, 2)
    os.close(logfd_err)

if 1:
    def mount_all_tgtpart():
        # Mount /proc.
        procpath = os.path.join(tgtsys_root, 'proc')
        if not os.path.isdir(procpath):
            os.makedirs(procpath)
        if not os.path.exists(os.path.join(procpath, 'cmdline')):
            isys.mount('proc', 'proc', procpath, 0, 0)
        # Mount /sys
        syspath = os.path.join(tgtsys_root, 'sys')
        if not os.path.isdir(syspath):
            os.makedirs(syspath)
        if not os.path.exists(os.path.join(syspath, 'block')):
            isys.mount('sysfs', 'sys', syspath, 0, 0)

        # Mount /dev
        if 1:
            devdir = os.path.join(tgtsys_root, 'dev')
            #dolog('mount bind device to target system.')
            if not os.path.isdir(devdir):
                os.makedirs(devdir)
            os.system('mount -o bind /dev %s' % devdir)

        return  0

    def umount_all_tgtpart():
        # Umount proc.
        procdir = os.path.join(tgtsys_root, 'proc')
        try:
            if os.path.exists(os.path.join(procdir, 'cmdline')):
                isys.umount(procdir)
        except Exception, errmsg:
            dolog('Umount %s failed: %s\n' % (procdir, str(errmsg)))
        # Umount sys.
        sysdir = os.path.join(tgtsys_root, 'sys')
        try:
            if os.path.exists(os.path.join(sysdir, 'block')):
                isys.umount(sysdir)
        except Exception, errmsg:
            dolog('Umount %s failed: %s\n' % (sysdir, str(errmsg)))

        # Umount /dev
        if 1:
            devdir = os.path.join(tgtsys_root, 'dev')
            #dolog('Umount bind device from target system.')
            os.system('umount %s' % devdir)

        return  0

ts = None
if 1:
    def rpm_installcb(what, bytes, total, h, data):
        global cur_rpm_fd
        if what == rpm.RPMCALLBACK_INST_OPEN_FILE:
            cur_rpm_fd = os.open(h, os.O_RDONLY)
            return  cur_rpm_fd
        elif what == rpm.RPMCALLBACK_INST_CLOSE_FILE:
            os.close(cur_rpm_fd)
        elif what == rpm.RPMCALLBACK_INST_PROGRESS:
            pass

    def package_install(operid, pkgname, firstpkg):
        #print 'package_install: ' + '\n' +str(operid) + '\n' + str(pkgname) + \
        #'\n' + str(firstpkg)
        global tgtsys_root
        global ts
        if ts is None:
            dolog('Create TransactionSet\n')
            ts = rpm.TransactionSet(tgtsys_root)
            #ts.setProbFilter(rpm.RPMPROB_FILTER_OLDPACKAGE |
            #                 rpm.RPMPROB_FILTER_REPLACENEWFILES |
            #                 rpm.RPMPROB_FILTER_REPLACEOLDFILES |
            #                 rpm.RPMPROB_FILTER_REPLACEPKG)
            ts.setProbFilter(~rpm.RPMPROB_FILTER_DISKSPACE)
            
            #ts.setVSFlags(rpm.RPMVSF_NODSA)
            #ts.setVSFlags(rpm.RPMVSF_NODSAHEADER)
            #ts.setVSFlags(rpm._RPMVSF_NOSIGNATURES)
            #ts.setVSFlags(-1)
            ts.setVSFlags(~(rpm.RPMVSF_NORSA | rpm.RPMVSF_NODSA))
            ts_flags_to_rpm = { 
                            'noscripts': rpm.RPMTRANS_FLAG_NOSCRIPTS,
                            'nopre': rpm.RPMTRANS_FLAG_NOPRE,
                            'nopost': rpm.RPMTRANS_FLAG_NOPOST,
                            'nopreun': rpm.RPMTRANS_FLAG_NOPREUN,
                            'nopostun': rpm.RPMTRANS_FLAG_NOPOSTUN,
                            'notriggers': rpm.RPMTRANS_FLAG_NOTRIGGERS,
                            'notriggerin': rpm.RPMTRANS_FLAG_NOTRIGGERIN,
                            'notriggerun': rpm.RPMTRANS_FLAG_NOTRIGGERUN,
                            'notriggerpostun': rpm.RPMTRANS_FLAG_NOTRIGGERPOSTUN,
                            'nodocs': rpm.RPMTRANS_FLAG_NODOCS,
                            'test': rpm.RPMTRANS_FLAG_TEST,
                            'justdb': rpm.RPMTRANS_FLAG_JUSTDB,
                            'repackage': rpm.RPMTRANS_FLAG_REPACKAGE}
            #curflags = ts.setFlags(0)
            #ts.setFlags(curflags | ts_flags_to_rpm['nopre'] |
            #        ts_flags_to_rpm['notriggers'])

            # rpm.setLogFile(instLog)   TO DO

            trans_flags = []
            if 0:
                trans_flags.append(rpm.RPMTRANS_FLAG_NOSUGGEST)

                trans_flags.append(rpm.RPMTRANS_FLAG_NOFILEDIGEST)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOMD5)

                trans_flags.append(rpm.RPMTRANS_FLAG_NOSCRIPTS)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOPRE)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOPREUN)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOPOST)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOPOSTUN)

                trans_flags.append(rpm.RPMTRANS_FLAG_NOTRIGGERS)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOTRIGGERIN)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOTRIGGERPREIN)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOTRIGGERUN)
                trans_flags.append(rpm.RPMTRANS_FLAG_NOTRIGGERPOSTUN)
                # -------------
                trans_flags.append(rpm.RPMTRANS_FLAG_JUSTDB)
                # -------------
                # -------------
                trans_flags.append(rpm.RPMTRANS_FLAG_ALLFILES)
                # -------------
                #trans_flags.append(rpm.RPMTRANS_FLAG_NOCONFIGS)
                #trans_flags.append(rpm.RPMTRANS_FLAG_NODOCS)
                #trans_flags.append(rpm.RPMTRANS_FLAG_TEST)
                
                #trans_flags.append(rpm.RPMTRANS_FLAG_ADDINDEPS)
                #trans_flags.append(rpm.RPMTRANS_FLAG_BUILD_PROBS)

                #trans_flags.append(rpm.RPMTRANS_FLAG_KEEPOBSOLETE)
                #trans_flags.append(rpm.RPMTRANS_FLAG_REPACKAGE)
                #trans_flags.append(rpm.RPMTRANS_FLAG_REVERSE)

            for tf in trans_flags:
                curflags = ts.setFlags(0)
                ts.setFlags(curflags | tf)
            #flag_anaconda = 0b100000000000000000000000000   # rpm.RPMTRANS_FLAG_ANACONDA 67108864
            #ts.setFlags(flag_anaconda)#rpm.RPMTRANS_FLAG_ANACONDA)			# have been removed from last rpm version

        pkgpath = os.path.join(os.path.dirname(firstpkg), pkgname)
        dolog('%s  pkg_install(%s, %s)\n' % (operid, pkgname, str(pkgpath)))
        try:
            rpmfd = os.open(pkgpath, os.O_RDONLY)
            hdr = ts.hdrFromFdno(rpmfd)
            ts.addInstall(hdr, pkgpath, 'i')
            os.close(rpmfd)
            # Sign the installing pkg name in stderr
            print >>sys.stderr, '%s ERROR :\n' % pkgname
            problems = ts.run(rpm_installcb, (operid))
            if problems:
                dolog('PROBLEMS in %s : %s\n' % (pkgname, str(problems)))
                # problems is a list that each elements is a tuple.
                # The first element of the tuple is a human-readable string
                # and the second is another tuple such as:
                #    (rpm.RPMPROB_FILE_CONFLICT, conflict_filename, 0L)
                return  problems
        except Exception, errmsg:
            dolog('FAILED in %s : %s\n' % (pkgname, str(errmsg)))
            return str(errmsg)
        return 0

# get the pkg list
g_map = {}
l_map = {}
try:
    execfile(pkgarr_path, g_map, l_map)
    arch_map = l_map['arch_map']
    arrangement = l_map['arrangement']
    archsize_map = l_map['archsize_map']
    pkgpos_map = l_map['pkgpos_map']
    toplevelgrp_map = l_map['toplevelgrp_map']
    del(g_map)
    del(l_map)
except:
    print 'Can not execute pkgarr.py file at %s' % pkgarr_path
    sys.exit(1)

pkgs_list = []      # all pkg list order by pkgarr.py
for cd in arrangement:
    for pkg in cd:
        pkgs_list.append(pkg[pkgpublic.path])
        #print pkg[pkgpublic.path]

# create target system root
if not os.path.exists(tgtsys_root):
    os.makedirs(tgtsys_root)

# mount virtual filesystem on target system
if arg_map.has_key('mount'):
    mount_all_tgtpart()

if 1:
    # This code is specific to rpm.
    var_lib_rpm = os.path.join(tgtsys_root, 'var/lib/rpm')
    if not os.path.isdir(var_lib_rpm):
        os.makedirs(var_lib_rpm)

    def run_bash(cmd, argv, root='/'):
        import subprocess
        def chroot():
            os.chroot(root)

        cmd_res = {}
        res = subprocess.Popen([cmd] + argv, 
                                stdout = subprocess.PIPE, 
                                stderr = subprocess.PIPE, 
                                preexec_fn = chroot, cwd = root,
                                close_fds = True)
        res.wait()
        cmd_res['out']=res.stdout.readlines()
        cmd_res['err']=res.stderr.readlines()
        cmd_res['ret']=res.returncode
        return cmd_res

def install_pkgs():
    import time
    start = time.time()
    for id in range(len(pkgs_list)):
        dolog( '%s %s\n' % (id, pkgs_list[id]) )
        # --nodigest --nosignature
        cmd = 'rpm'
        argv = ['-i', '--noorder','--nosuggest',
                '--force','--nodeps',
                '--root',
                tgtsys_root,
                os.path.join(pkgs_root, pkgs_list[id]),
            ]
        cmd_res = run_bash(cmd, argv)
        #print cmd_res['out']
        #print cmd_res['err']
        #print cmd_res['ret']
        if cmd_res['err'] != []:
            dolog('Warning pkg: %s\n%s' % (pkgs_list[id], ''.join(cmd_res['err'])))
        if cmd_res['ret'] != 0:
            dolog('Error pkg %s\nexit code:\n %s\nOUTPUT:\n%s\nERROROUT:\n%s\n' \
                   %(pkgs_list[id], cmd_res['ret'], ''.join(cmd_res['out']), ''.join(cmd_res['err'])) )
        #package_install( id, pkgs_list[id], os.path.join(pkgs_root ,pkgs_list[0]) )
    finish = time.time()
    dolog('Use %s time' % str(finish - start))

# install pkg start
if not arg_map.has_key('mount') and not arg_map.has_key('umount'):
    mount_all_tgtpart()
    install_pkgs()
    umount_all_tgtpart()
# install pkg end

# umount virtual filesystem from target system
if arg_map.has_key('umount'):
    umount_all_tgtpart()

# Documents from http://rpm.org/api/4.7.0/group__rpmts.htm
#RPMTRANS_FLAG_NONE 	
#RPMTRANS_FLAG_TEST 	from --test
#RPMTRANS_FLAG_BUILD_PROBS 	don't process payload
#RPMTRANS_FLAG_NOSCRIPTS 	from --noscripts
#RPMTRANS_FLAG_JUSTDB 	from --justdb
#RPMTRANS_FLAG_NOTRIGGERS 	from --notriggers
#RPMTRANS_FLAG_NODOCS 	from --excludedocs
#RPMTRANS_FLAG_ALLFILES 	from --allfiles
#RPMTRANS_FLAG_KEEPOBSOLETE 	
#
#Todo:
#    Document. 
#
#RPMTRANS_FLAG_NOCONTEXTS 	from --nocontexts
#RPMTRANS_FLAG_DIRSTASH 	obsolete, unused
#RPMTRANS_FLAG_REPACKAGE 	obsolete, unused
#RPMTRANS_FLAG_PKGCOMMIT 	
#RPMTRANS_FLAG_PKGUNDO 	
#RPMTRANS_FLAG_COMMIT 	
#RPMTRANS_FLAG_UNDO 	
#RPMTRANS_FLAG_REVERSE 	
#RPMTRANS_FLAG_NOTRIGGERPREIN 	from --notriggerprein
#RPMTRANS_FLAG_NOPRE 	from --nopre
#RPMTRANS_FLAG_NOPOST 	from --nopost
#RPMTRANS_FLAG_NOTRIGGERIN 	from --notriggerin
#RPMTRANS_FLAG_NOTRIGGERUN 	from --notriggerun
#RPMTRANS_FLAG_NOPREUN 	from --nopreun
#RPMTRANS_FLAG_NOPOSTUN 	from --nopostun
#RPMTRANS_FLAG_NOTRIGGERPOSTUN 	from --notriggerpostun
#RPMTRANS_FLAG_NOPAYLOAD 	
#RPMTRANS_FLAG_APPLYONLY 	
#RPMTRANS_FLAG_NOMD5 	from --nomd5
#RPMTRANS_FLAG_NOFILEDIGEST 	from --nofiledigest (alias to --nomd5)
#RPMTRANS_FLAG_NOSUGGEST 	from --nosuggest
#RPMTRANS_FLAG_ADDINDEPS 	from --aid
#RPMTRANS_FLAG_NOCONFIGS 	from --noconfigs
#RPMTRANS_FLAG_DEPLOOPS 	from --deploops 
